package basisService

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/Sir-yuChen/funAnalyse/global"
	"github.com/Sir-yuChen/funAnalyse/global/constant"
	"github.com/Sir-yuChen/funAnalyse/global/redisEnums"
	"github.com/Sir-yuChen/funAnalyse/model"
	"github.com/Sir-yuChen/funAnalyse/model/common/ApiReturn"
	"github.com/Sir-yuChen/funAnalyse/model/request"
	"github.com/Sir-yuChen/funAnalyse/model/resp"
	"github.com/Sir-yuChen/funAnalyse/service/casbinService"
	"github.com/Sir-yuChen/funAnalyse/service/commonService"
	"github.com/Sir-yuChen/funAnalyse/utils"
	"github.com/Sir-yuChen/funAnalyse/utils/socket"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"strings"
	"time"
)

type UserService struct{}

func NewUserService() *UserService {
	//创建一个JWT对象 存储配置文件中 配置为Jwt全局签名
	return &UserService{}
}

// UserLogin 用户登录
func (u UserService) UserLogin(userLogin *request.UserLogin, c *gin.Context) (a ApiReturn.ApiReturnCode) {
	var user model.StockUser
	var roleIds []model.StockCasbinRoleRelationUserModel
	//账号密码登录方式
	if userLogin.LoginType == "pwd" {
		//1. 校验用户是否存在
		err := global.GVA_DB.Where("customer_name = ? ", userLogin.CustomerName).First(&user).Error
		if err != nil {
			global.GVA_LOG.Error("用户信息不存在", zap.String("customerName", userLogin.CustomerName))
			return ApiReturn.NoUserInfo
		}
		//2.校验密码 参数：前端 /db
		isOk, _ := utils.ValidatePassword(userLogin.LoginPassword, user.LoginPassword)
		if !isOk {
			global.GVA_LOG.Error("密码校验失败", zap.Any("userLogin", userLogin))
			return ApiReturn.ErrPwd
		}
		//查用户角色 放缓存
		if e := global.GVA_DB.Model(model.StockCasbinRoleRelationUserModel{}).Where(" user_id = ? ", user.CustomerId).Find(&roleIds).Error; e != nil {
			global.GVA_LOG.Error("登录查用户角色异常", zap.Any("userLogin", userLogin), zap.Error(e))
			return ApiReturn.ErrSystem
		}
		if len(roleIds) != 0 {
			timer := time.Duration(global.GVA_CONFIG.JWT.RedisExpiresTime) * time.Second
			//角色放缓存中
			jsonV, _ := json.Marshal(roleIds)
			result, err := global.GVA_REDIS.Set(context.Background(), user.CustomerId+redisEnums.USER_ROLE_KEY_SUFFIX, jsonV, timer).Result()
			if err != nil || result != "OK" {
				global.GVA_LOG.Error("登录 redis 用户角色信息", zap.Any("userLogin", userLogin), zap.Error(err))
				return ApiReturn.ErrSystem
			}
		}
		//3.生成token
		apiReturn := u.CreateUserToken(&user)
		if apiReturn.Code != ApiReturn.OK.Code {
			return apiReturn
		}
		token := apiReturn.Data
		apiReturn.Data = resp.UserResponse{
			User:  user,
			Token: fmt.Sprintf("%s", token),
		}
		c.Header("X-Token", fmt.Sprintf("%s", token))
		defer func() {
			if err != nil {
				global.GVA_REDIS.Del(context.Background(), user.CustomerId+redisEnums.USER_ROLE_KEY_SUFFIX) //清除角色缓存
				global.GVA_REDIS.Del(context.Background(), fmt.Sprintf("%s", token))                        //清除token缓存
			}
		}()
		return apiReturn
	}
	//todo 可扩展手机号登录方式 短信验证
	if userLogin.LoginType == "mobile" {
	}
	return ApiReturn.ErrSystem
}

// UserRegister 用户注册
func (u UserService) UserRegister(registerInfo *request.UserRegister) (a ApiReturn.ApiReturnCode) {
	var total int64
	var apiReturn ApiReturn.ApiReturnCode
	var userResponse resp.UserResponse
	var user model.StockUser
	//1. 校验用户名是否重复
	txErr := global.GVA_DB.Transaction(func(tx *gorm.DB) error {
		tx.Model(model.StockUser{}).Where("customer_name = ?", registerInfo.CustomerName).Count(&total)
		if total > 0 {
			global.GVA_LOG.Error("用户名已存在", zap.Any("registerInfo", registerInfo))
			apiReturn := ApiReturn.UserNameExisted
			apiReturn.Data = registerInfo
			return errors.New(ApiReturn.UserNameExisted.Msg)
		}
		tx.Model(model.StockUser{}).Where("mobile_no = ?", registerInfo.MobileNo).Count(&total)
		//2. 手机号去重
		if total > 0 {
			global.GVA_LOG.Error("手机号已注册", zap.Any("registerInfo", registerInfo))
			apiReturn := ApiReturn.UserPhoneExisted
			apiReturn.Data = registerInfo
			return errors.New(ApiReturn.UserPhoneExisted.Msg)
		}
		//对密码加密
		password, err := utils.GeneratePassword(registerInfo.LoginPassword)
		if err != nil {
			global.GVA_LOG.Error("密码加密异常", zap.Any("registerInfo", registerInfo))
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		r := casbinService.NewStockCasbinRoleService().GetStockCasbinRole(&model.StockCasbinRoleModel{
			RoleName: "generalRole",
		})
		if r.Code != ApiReturn.OK.Code || r.Data == nil {
			global.GVA_LOG.Error("注册 未查询到默认角色")
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		// 组装参数 注册信息
		// V1 基于时间
		uid, _ := uuid.NewUUID()
		nowTime := model.LocalTime(time.Now())
		var userInfo = &model.StockUser{
			CustomerId:      strings.Replace(uid.String(), "-", "", -1),
			CustomerName:    registerInfo.CustomerName,
			MobileNo:        registerInfo.MobileNo,
			Email:           registerInfo.Email,
			Status:          registerInfo.Status,
			LoginPassword:   string(password),
			RegisterDate:    &nowTime,
			RegisterChannel: registerInfo.RegisterChannel,
			LoginDate:       &nowTime,
			CreateTime:      &nowTime,
			LastUpdTime:     &nowTime,
		}
		c := tx.Create(userInfo).Error
		if c != nil {
			global.GVA_LOG.Error("注册新增用户信息异常", zap.Any("registerInfo", registerInfo), zap.Error(c))
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		roleModel := r.Data.([]model.StockCasbinRoleModel)
		for _, v := range roleModel {
			//配置默认角色
			relationUser := casbinService.NewStockCasbinRoleRelationUserService().InsertStockCasbinRoleRelationUser(&model.StockCasbinRoleRelationUserModel{
				RoleId: v.Id,
				UserId: userInfo.CustomerId,
				Status: "Y",
			})
			if relationUser.Code != ApiReturn.OK.Code {
				global.GVA_LOG.Error("注册 用户角色关系建立失败")
			}
		}
		var roleIds []model.StockCasbinRoleRelationUserModel
		//查用户角色 放缓存
		if e := tx.Model(model.StockCasbinRoleRelationUserModel{}).Where(" user_id = ? ", user.CustomerId).Find(&roleIds).Error; e != nil {
			global.GVA_LOG.Error("注册 查用户角色关联关系 异常", zap.Any("registerInfo", registerInfo), zap.Error(e))
			return errors.New(ApiReturn.ErrSystem.Msg)
		}
		if roleIds != nil {
			timer := time.Duration(global.GVA_CONFIG.JWT.RedisExpiresTime) * time.Second
			//角色放缓存中
			jsonV, _ := json.Marshal(roleIds)
			global.GVA_REDIS.Set(context.Background(), user.CustomerId+redisEnums.USER_ROLE_KEY_SUFFIX, jsonV, timer)
		}
		return nil
	})
	if txErr != nil {
		return ApiReturn.ApiReturnCode{
			Code: ApiReturn.TxErrSystem.Code,
			Data: nil,
			Msg:  txErr.Error(),
		}
	}
	//3.注册成功自动登录生成token
	apiReturn = u.CreateUserToken(&user)
	if apiReturn.Code != ApiReturn.OK.Code {
		return apiReturn
	}
	token := apiReturn.Data
	userResponse.User = user
	userResponse.Token = fmt.Sprintf("%s", token)
	userResponse.NeedLogin = "N"
	apiReturn.Data = userResponse
	return apiReturn
}

// GetUserInfo 查询用户详情
func (userService *UserService) GetUserInfo(customerId string) (user model.StockUser, err error) {
	var reqUser model.StockUser
	err = global.GVA_DB.Where("customer_id = ?", customerId).First(&reqUser).Error
	if err != nil {
		global.GVA_LOG.Error("获取用户信息异常", zap.String("customerId", customerId))
		return reqUser, err
	}
	global.GVA_LOG.Info("获取用户信息", zap.String("customerId", customerId), zap.Any("StockUser", reqUser))
	return reqUser, err
}

func (u UserService) UpateUserInfo(m *request.UpdateUserInfo, c *gin.Context) (a ApiReturn.ApiReturnCode) {
	//获取操作账户信息
	userInfo, err := commonService.GetUserInfoByToken(c)
	if err != nil {
		global.GVA_LOG.Error("获取操作账户信息异常", zap.Any("request", m))
		return ApiReturn.ErrSystem
	}
	//校验验证码
	verCode := request.VerificationCodeRequest{
		MobileNo:         userInfo.MobileNo,
		VerificationCode: m.VerificationCode,
		IpAddr:           m.IpAddr,
		Type:             constant.VER_CODE_MOBILE,
	}
	apiReturnCode := NewCommonService().CheckVerificationCode(&verCode)
	if apiReturnCode.Code != ApiReturn.OK.Code {
		return apiReturnCode
	}
	//修改用户信息
	switch m.Type {
	case constant.UPDATE_USER_TYPE_MOBILE: //修改手机号
		return u.UpdateUserPhone(m, userInfo, c)
	case constant.UPDATE_USER_TYPE_PWD: //修改密码
		return u.UpdateUserPwd(m, userInfo, c)
	default:
		global.GVA_LOG.Error("未知的修改信息类型", zap.Any("request", m))
		return ApiReturn.DefinedNotType
	}
	return ApiReturn.OK
}
func (u UserService) UpdateUserPhone(m *request.UpdateUserInfo, userInfo *model.StockUser, c *gin.Context) ApiReturn.ApiReturnCode {
	//校验修改的手机号是否和原来的手机号相同
	var dbUser model.StockUser
	if err := global.GVA_DB.Model(model.StockUser{}).Where(" customer_id = ? ", userInfo.CustomerId).First(&dbUser).Error; err != nil {
		global.GVA_LOG.Error("查询账户信息异常", zap.Any("request", m), zap.Any("CustomerId", userInfo.CustomerId), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	if dbUser.MobileNo == m.MobileNo {
		global.GVA_LOG.Error("账户修改手机号前后不能相同", zap.Any("request", m), zap.Any("dbUser", dbUser))
		return ApiReturn.UpdateInfoSame
	}
	//校验新手机号是否已经被注册过账户
	var total int64
	if err := global.GVA_DB.Model(model.StockUser{}).Where(" mobile_no = ? ", m.MobileNo).Count(&total).Error; err != nil {
		global.GVA_LOG.Error("校验新手机号是否已经被注册过", zap.Any("MobileNo", m.MobileNo), zap.Any("CustomerId", userInfo.CustomerId), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	if total > 0 {
		return ApiReturn.UserPhoneExisted
	}
	dbUser.MobileNo = m.MobileNo
	if err := global.GVA_DB.Model(model.StockUser{}).Where(" customer_id = ? ", userInfo.CustomerId).Updates(&dbUser).Error; err != nil {
		global.GVA_LOG.Error("校验通过，修改手机号异常", zap.Any("request", m), zap.Any("CustomerId", userInfo.CustomerId), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	token, _ := c.Get("token")
	//删除缓存token,从新登录
	global.GVA_REDIS.Del(context.Background(), fmt.Sprint(token))
	return ApiReturn.UpdateReLogin
}

func (u UserService) UpdateUserPwd(m *request.UpdateUserInfo, userInfo *model.StockUser, c *gin.Context) ApiReturn.ApiReturnCode {
	if m.LoginPassword == "" {
		return ApiReturn.ErrParam
	}
	//校验修改的密码是否相同
	var dbUser model.StockUser
	if err := global.GVA_DB.Model(model.StockUser{}).Where(" customer_id = ? ", userInfo.CustomerId).First(&dbUser).Error; err != nil {
		global.GVA_LOG.Error("查询账户信息异常", zap.Any("request", m), zap.Any("CustomerId", userInfo.CustomerId), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	password, err := utils.GeneratePassword(m.LoginPassword)
	if err != nil {
		global.GVA_LOG.Error("密码加密异常", zap.Any("customer_id", userInfo.CustomerId))
		return ApiReturn.ErrSystem
	}
	if dbUser.LoginPassword == string(password) {
		global.GVA_LOG.Error("新密码与旧密码不能相同", zap.Any("request", m), zap.Any("dbUser", dbUser))
		return ApiReturn.UpdateInfoSame
	}
	dbUser.LoginPassword = string(password)
	if err := global.GVA_DB.Model(model.StockUser{}).Where(" customer_id = ? ", userInfo.CustomerId).Updates(&dbUser).Error; err != nil {
		global.GVA_LOG.Error("校验通过,修改密码异常", zap.Any("request", m), zap.Any("CustomerId", userInfo.CustomerId), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	token, _ := c.Get("token")
	//删除缓存token,从新登录
	global.GVA_REDIS.Del(context.Background(), fmt.Sprint(token))
	return ApiReturn.UpdateReLogin
}

// CreateUserToken CreateToken 签发登录token
func (u UserService) CreateUserToken(user *model.StockUser) (a ApiReturn.ApiReturnCode) {
	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
	var claims = request.BaseClaims{
		CustomerName: user.CustomerName,
		CustomerId:   user.CustomerId,
		UserStatus:   user.Status,
	}
	c := j.CreateClaims(claims)
	token, err := j.CreateToken(c)
	if err != nil {
		global.GVA_LOG.Error("token生成异常", zap.String("customerName", user.CustomerName), zap.Error(err))
		return ApiReturn.ErrCreateToken
	}
	//token redisEnums
	if !global.GVA_CONFIG.System.UseMultipoint {
		ok := ApiReturn.OK
		tokenMap := make(map[string]string)
		tokenMap["token"] = token
		ok.Data = token
		return ok
	}

	b, err := utils.RedisJWTExists(token)
	if err != nil {
		return ApiReturn.ErrSystem
	}
	if b != 0 {
		global.GVA_REDIS.Del(context.Background(), token)
	}
	userJson, errs := json.Marshal(user) //转换成JSON返回的是byte[]
	if errs != nil {
		fmt.Println(errs.Error())
	}
	t, err := utils.SetRedisJWT(token, string(userJson))
	if err != nil || !t {
		global.GVA_LOG.Error("token--->redisEnums 异常", zap.String("customerName", user.CustomerName), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	ok := ApiReturn.OK
	ok.Data = token
	return ok
}

func (u UserService) UserLoginOut(c *gin.Context) ApiReturn.ApiReturnCode {
	//用戶退登
	user, err := commonService.GetUserInfoByToken(c)
	if err != nil {
		global.GVA_LOG.Error("退登 获取操作账户信息异常", zap.Error(err))
		return ApiReturn.ErrSystem
	}
	token, _ := c.Get("token")
	//清除token
	global.GVA_REDIS.Del(context.Background(), fmt.Sprint(token))
	//清除角色权限信息 使用通配符删除操作账户相关的key
	ctx := context.Background()
	iter := global.GVA_REDIS.Scan(ctx, 0, user.CustomerId+"*", 0).Iterator()
	for iter.Next(ctx) {
		err := global.GVA_REDIS.Del(ctx, iter.Val()).Err()
		if err != nil {
			global.GVA_LOG.Error("退登 删除操作信息key异常", zap.Error(err))
		}
	}
	//关闭长连接
	socket.CloseWebSocketConnection(user.CustomerId)
	return ApiReturn.OK
}

// GetUserList 查用户集合或导出
func (u UserService) GetUserList(req *request.UserInfoList, c *gin.Context) (a ApiReturn.ApiReturnCode) {
	apireturn := ApiReturn.OK
	var mo []model.StockUser
	var ex request.ExportExcelInfo
	sql := ``
	obj := model.NewStockUserModel()
	tableName := obj.TableName()
	sqlCondition := ""
	if req.User != (model.StockUser{}) {
		sqlCondition = utils.ReflectHandleStuct(&req.User)
	}
	if req.AllField == constant.STATUS_N && req.Fields != "" {
		sql += `select ` + req.Fields + ` from `
		ex.AllField = false
		ex.Fields = req.Fields
	} else {
		//查所有字段
		sql += `select * from `
		ex.AllField = true
	}
	if sqlCondition != "" {
		sql += tableName + ` where ` + sqlCondition + ` order by id desc `
	} else {
		sql += tableName + ` order by id desc`
	}
	err := global.GVA_DB.Raw(sql).Scan(&mo).Error
	if err != nil {
		global.GVA_LOG.Error("GetUserList 查用户集合异常", zap.Any("req", req), zap.Any("sql", sql), zap.Error(err))
		return ApiReturn.ErrSystem
	}
	if len(mo) == 0 {
		apireturn.Msg = ApiReturn.NoData.Msg
		return apireturn
	}
	apireturn.Data = mo
	//是否导出
	if req.Export == constant.STATUS_Y {
		moLeng := len(mo)
		ex.ExportNumber = moLeng
		ex.TableName = tableName
		data := make([]map[string]interface{}, 0)
		for _, user := range mo {
			itemMap := utils.ReflectHandleStuctMap(&user)
			data = append(data, itemMap)
		}
		ex.ExportDatas = data
		apireturn = commonService.ExportToExcel(&ex, c)
		if apireturn.Code != ApiReturn.OK.Code {
			global.GVA_LOG.Error("GetUserList 查询用户集合导出数据失败", zap.Any("apireturn", apireturn))
		}

	}
	return apireturn
}
